#include <stdio.h>
#include <stdlib.h>

typedef enum { false, true } bool;
typedef int Vertex; /* 顶点编号类型 */
typedef int GElemSet; /* 边权重类型 */
typedef char VertInfo; /* 顶点信息类型 */
typedef struct EdgeNode *Position; /* 指针即结点位置 */
struct EdgeNode {
    Vertex dest; /* 边的另一端点编号 */
    GElemSet weight; /* 权重 */
    Position next; /* 线性表中下一个元素的位置 */
};
typedef struct HeadNode *AdjList; /* 邻接表 */
struct HeadNode {
    Position adj; /* 邻接表头指针 */
    VertInfo data; /* 存储顶点信息 */
};
typedef struct LGraphNode *LGraph; /* 邻接表表示的图 */
struct LGraphNode {
    int n_verts; /* 顶点数 */
    int m_edges; /* 边数 */
    AdjList *ver_list; /* 存储顶点邻接表 */
    bool directed; /* true为有向图，false为无向图 */
};
#define NIL -1 /* 生成树不存在时的返回值 */
#define kMaxV 1000 /* 最大顶点数量 */
void InitGraph(LGraph graph, int kMaxVertex, bool directed);
bool ExistEdge(LGraph graph, Vertex u, Vertex v);
void InsertEdge(LGraph graph, Vertex u, Vertex v, GElemSet weight);
LGraph BuildGraph();

typedef struct Edge *EdgeSet;
struct Edge {
    Vertex u, v; /* 边的两个端点 */
    GElemSet weight; /* 权重 */
};
typedef Vertex *VertexSet;

int Cmp_Weight(const void *a, const void *b) {
    /* 用于按权重非递减排序的比较函数 */
    return (((const struct Edge*)a)->weight
            < ((const struct Edge*)b)->weight) ?
           -1 : 1;
}

EdgeSet InitEdgeSet(LGraph graph) {
    /* 初始化边集合 */
    EdgeSet e;
    Vertex u, v;
    Position p;
    int i;

    e = (EdgeSet)malloc(sizeof(struct Edge) * graph->m_edges);
    for (u = 0, i = 0; u < graph->n_verts; u++) {
        p = graph->ver_list[u]->adj; /* 遍历u的邻接表 */
        while (p != NULL) {
            /* 避免重复录入无向图的边，只收u<v的边 */
            if (u < p->dest) {
                e[i].u = u;
                e[i].v = p->dest;
                e[i].weight = p->weight;
                i++;
            }
            p = p->next;
        }
    }
    /* 将边集合按权重非递减排序 */
    qsort(e, graph->m_edges, sizeof(struct Edge), Cmp_Weight);
    return e;
}

VertexSet InitVertexSet(LGraph graph) {
    /* 初始化顶点连通分量集合 */
    /* v[u]=w表示u和w属于同一个集合 */
    /* v[u]=u表示u所属集合的名称是u */
    VertexSet v;
    Vertex u;

    v = (VertexSet)malloc(sizeof(Vertex) * graph->n_verts);
    for (u = 0; u < graph->n_verts; u++) {
        v[u] = u; /* 初始状态下每个连通分量只有一个顶点 */
    }
    return v;
}

Vertex Find(VertexSet v, Vertex u) {
    /* 找出u所在集合的名称 */
    while (v[u] != u) { /* 如果u不是所在集合的名称 */
        u = v[u]; /* 检查同集合的另一个顶点 */
    }
    return u;
}

bool IsCycle(VertexSet v, Vertex v1, Vertex v2) {
    Vertex name1, name2;

    name1 = Find(v, v1); /* 得到v1所属的连通集名称 */
    name2 = Find(v, v2); /* 得到v2所属的连通集名称 */
    if (name1 == name2) { /* 两端点已经属于同一集合 */
        return true; /* 边(v1, v2)的加入将构成回路 */
    } else { /* 不构成回路，将两个连通集合并 */
        v[name2] = name1; /*将集合2重命名为集合1 */
        return false; /* 不构成回路 */
    }
}

GElemSet Kruskal(LGraph graph) {
    GElemSet weight;
    int cnt_edge, i;
    EdgeSet e;
    VertexSet v;

    e = InitEdgeSet(graph); /* 初始化边集合 */
    v = InitVertexSet(graph); /* 初始化顶点连通分量集合 */
    weight = 0;
    cnt_edge = 0;
    i = 0;
    while (cnt_edge < (graph->n_verts - 1)) {
        /* 当收集的边不足以构成树时 */
        if (i == graph->m_edges) { /* 边集已空 */
            break; /* 跳出循环 */
        }
        if (IsCycle(v, e[i].u, e[i].v) == false) {
            /* 若边e[i]的加入不构成回路 */
            weight += e[i].weight; /* 收录e[i] */
            cnt_edge ++;
        }
        i++; /* 考察下一条边 */
    }
    if (cnt_edge < (graph->n_verts - 1)) {
        weight = NIL; /* 设置错误标记，表示生成树不存在 */
    }
    return weight;
}

int main(void) {
    printf("%d\n", Kruskal(BuildGraph()));
    return 0;
}

void InitGraph(LGraph graph, int kMaxVertex, bool directed) {
    /* 初始化一个空的图 */
    Vertex v;
    Position p;

    graph->n_verts = 0;
    graph->m_edges = 0;
    /* 声明邻接表头结点数组graph->ver_list[kMaxVertex] */
    graph->ver_list = (AdjList *)malloc(sizeof(AdjList) * kMaxVertex);
    for (v = 0; v < kMaxVertex; v++) {
        graph->ver_list[v] = (AdjList)malloc(sizeof(struct HeadNode));
        graph->ver_list[v]->adj = NULL;
    }
    graph->directed = directed;
}

bool ExistEdge(LGraph graph, Vertex u, Vertex v) {
    Position p;
    bool ret = false;

    if (u < graph->n_verts && v < graph->n_verts) {
        p = graph->ver_list[u]->adj;
        while (p != NULL && p->dest != v) {
            p = p->next;
        }
        if (p != NULL) {
            ret = true;
        }
    }
    return ret;
}

void InsertEdge(LGraph graph, Vertex u, Vertex v, GElemSet weight) {
    Position p;

    if (ExistEdge(graph, u, v) == false) {
        p = (Position)malloc(sizeof(struct EdgeNode));
        p->dest = v;
        p->weight = weight;
        p->next = graph->ver_list[u]->adj;
        graph->ver_list[u]->adj = p;
        graph->m_edges++;
        if (graph->directed ==
                    false) { /* 如果是无向图，还要将u插入v的边表中 */
            p = (Position)malloc(sizeof(struct EdgeNode));
            p->dest = u;
            p->weight = weight;
            p->next = graph->ver_list[v]->adj;
            graph->ver_list[v]->adj = p;
        }
    }
}

LGraph BuildGraph() {
    LGraph graph;
    int n, m, i;
    Vertex u, v;
    GElemSet weight;

    graph = (LGraph)malloc(sizeof(struct LGraphNode));
    InitGraph(graph, kMaxV, false);
    scanf("%d %d\n", &n, &m);
    graph->n_verts = n;
    for (i = 0; i < m; i++) {
        scanf("%d %d %d\n", &u, &v, &weight);
        InsertEdge(graph, u - 1, v - 1, weight);
    }
    return graph;
}